www.gusucode.com > VC++ Canny算子提取边缘算法示例-源码程序 > VC++ Canny算子提取边缘算法示例-源码程序/code/用Canny算子提取边缘/源代码/SegApi.cpp
#include "GlobalApi.h" #include "stdafx.h" #include "cdib.h" #include "math.h" #include <direct.h> #include <complex> using namespace std; /************************************************************************* * * \函数名称: * RegionSegFixThreshold() * * \输入参数: * CDib * pDib - 指向CDib类的指针,含有原始图象信息 * int nThreshold - 区域分割的阈值 * * \返回值: * 无 * * \说明: * 1(逻辑)表示对应象素为前景区域,0表示背景 * 阈值分割的关键问题在于阈值的选取。阈值的选取一般应该视实际的应用而 * 灵活设定。 * ************************************************************************* */ void RegionSegFixThreshold(CDib * pDib, int nThreshold) { //遍历图象的纵坐标 int y; //遍历图象的横坐标 int x; //图象的长宽大小 CSize sizeImage = pDib->GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; //图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); //图像在内存中每一行象素占用的实际空间 int nSaveWidth = sizeImageSave.cx; //图像数据的指针 LPBYTE pImageData = pDib->m_lpImage; for(y=0; y<nHeight ; y++ ) for(x=0; x<nWidth ; x++ ) { if( *(pImageData+y*nSaveWidth+x) < nThreshold) *(pImageData+y*nSaveWidth+x) = 0; } } /************************************************************************* * * \函数名称: * RegionSegAdaptive() * * \输入参数: * CDib * pDib - 指向CDib类的指针,含有原始图象信息 * * \返回值: * 无 * * \说明: * 1(逻辑)表示对应象素为前景区域,0表示背景 * 阈值分割的关键问题在于阈值的选取。阈值的选取一般应该视实际的应用而 * 灵活设定。本函数中,阈值不是固定的,而是根据图象象素的实际性质而设定的。 * 这个函数把图像分成四个子图象,然后计算每个子图象的均值,根据均值设置阈值 * 阈值只是应用在对应的子图象 * ************************************************************************* */ void RegionSegAdaptive(CDib * pDib) { //遍历图象的纵坐标 int y; //遍历图象的横坐标 int x; //图象的长宽大小 CSize sizeImage = pDib->GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; //图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); //图像在内存中每一行象素占用的实际空间 int nSaveWidth = sizeImageSave.cx; //图像数据的指针 LPBYTE lpImage = pDib->m_lpImage; // 局部阈值 int nThd[2][2] ; // 子图象的平均值 int nLocAvg ; // 对左上图像逐点扫描: nLocAvg = 0 ; // y方向 for(y=0; y<nHeight/2 ; y++ ) { // x方向 for(x=0; x<nWidth/2 ; x++ ) { nLocAvg += lpImage[y*nSaveWidth + x]; } } // 计算均值 nLocAvg /= ( (nHeight/2) * (nWidth/2) ) ; // 设置阈值为子图象的平均值 nThd[0][0] = nLocAvg ; // 对左上图像逐点扫描进行分割: // y方向 for(y=0; y<nHeight/2 ; y++ ) { // x方向 for(x=0; x<nWidth/2 ; x++ ) { if(lpImage[y*nSaveWidth + x]<nThd[0][0]) lpImage[y*nSaveWidth + x] = 255 ; else { lpImage[y*nSaveWidth + x] = 0 ; } } } // ============================================= // 对左下图像逐点扫描: nLocAvg = 0 ; // y方向 for(y=nHeight/2; y<nHeight ; y++ ) { // x方向 for(x=0; x<nWidth/2 ; x++ ) { nLocAvg += lpImage[y*nSaveWidth + x]; } } // 计算均值 nLocAvg /= ( (nHeight - nHeight/2) * (nWidth/2) ) ; // 设置阈值为子图象的平均值 nThd[1][0] = nLocAvg ; // 对左下图像逐点扫描进行分割: // y方向 for(y=nHeight/2; y<nHeight ; y++ ) { // x方向 for(x=0; x<nWidth/2 ; x++ ) { if(lpImage[y*nSaveWidth + x]<nThd[1][0]) lpImage[y*nSaveWidth + x] = 255 ; else { lpImage[y*nSaveWidth + x] = 0 ; } } } // ============================================= // 对右上图像逐点扫描: nLocAvg = 0 ; // y方向 for(y=0; y<nHeight/2 ; y++ ) { // x方向 for(x=nWidth/2; x<nWidth ; x++ ) { nLocAvg += lpImage[y*nSaveWidth + x]; } } // 计算均值 nLocAvg /= ( (nHeight/2) * (nWidth - nWidth/2) ) ; // 设置阈值为子图象的平均值 nThd[0][1] = nLocAvg ; // 对右上图像逐点扫描进行分割: // y方向 for(y=0; y<nHeight/2 ; y++ ) { // x方向 for(x=nWidth/2; x<nWidth ; x++ ) { if(lpImage[y*nSaveWidth + x]<nThd[0][1]) lpImage[y*nSaveWidth + x] = 255 ; else { lpImage[y*nSaveWidth + x] = 0 ; } } } // ============================================= // 对右下图像逐点扫描: nLocAvg = 0 ; // y方向 for(y=nHeight/2; y<nHeight ; y++ ) { // x方向 for(x=nWidth/2; x<nWidth ; x++ ) { nLocAvg += lpImage[y*nSaveWidth + x]; } } // 计算均值 nLocAvg /= ( (nHeight - nHeight/2) * (nWidth - nWidth/2) ) ; // 设置阈值为子图象的平均值 nThd[1][1] = nLocAvg ; // 对右下图像逐点扫描进行分割: // y方向 for(y=nHeight/2; y<nHeight ; y++ ) { // x方向 for(x=nWidth/2; x<nWidth ; x++ ) { if(lpImage[y*nSaveWidth + x]<nThd[1][1]) lpImage[y*nSaveWidth + x] = 255 ; else { lpImage[y*nSaveWidth + x] = 0 ; } } } // 为了显示方便显示,逻辑1用黑色显示,逻辑0用白色显示 for(y=0; y<nHeight ; y++ ) { // x方向 for(x=0; x<nWidth ; x++ ) { lpImage[y*nSaveWidth + x] = 255 - lpImage[y*nSaveWidth + x] ; } } } /************************************************************************* * * \函数名称: * RobertsOperator() * * \输入参数: * CDib * pDib - 指向CDib类的指针,含有原始图象信息 * double * pdGrad - 指向梯度数据的指针,含有图像的梯度信息 * * \返回值: * 无 * * \说明: * Roberts算子 * ************************************************************************* */ void RobertsOperator(CDib * pDib, double * pdGrad) { // 遍历图象的纵坐标 int y; // 遍历图象的横坐标 int x; // 图象的长宽大小 CSize sizeImage = pDib->GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; // 图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); // 图像在内存中每一行象素占用的实际空间 int nSaveWidth = sizeImageSave.cx; // 图像数据的指针 LPBYTE pImageData = pDib->m_lpImage; // 初始化 for(y=0; y<nHeight ; y++ ) for(x=0 ; x<nWidth ; x++ ) { *(pdGrad+y*nWidth+x)=0; } // 下面开始利用Roberts算子进行计算,为了保证计算所需要的 // 的数据位于图像数据的内部,下面的两重循环的条件是 // y<nHeight-1 而不是y<nHeight,相应的x方向也是x<nWidth-1 // 而不是x<nWidth //这两个变量用来表示Roberts算子第一个模板的两个象素值 int nUpLeft; int nDownRight; // 这两个变量用来表示Roberts算子第二个模板的两个象素值 int nUpRight; int nDownLeft; // 这两个变量用来表示Roberts算子计算的结果 int nValueOne; int nValueTwo; // 临时变量 double dGrad; for(y=0; y<nHeight-1 ; y++ ) for(x=0 ; x<nWidth-1 ; x++ ) { // Roberts算子第一个模板需要的象素值 nUpLeft =*(pImageData+y*nSaveWidth+x) ; nDownRight =*( pImageData+(y+1)*nSaveWidth+x+1 ); nDownRight *=-1; //Roberts算子的第一个模板计算结果 nValueOne =nUpLeft+nDownRight ; // Roberts算子第二个模板需要的象素值 nUpRight =*( pImageData+y*nSaveWidth+x+1 ) ; nDownLeft =*( pImageData+(y+1)*nSaveWidth+x ); nDownLeft *=-1; // Roberts算子的第二个模板计算结果 nValueTwo =nUpRight+nDownLeft; // 计算两个偏导数的平方和 dGrad=nValueOne*nValueOne + nValueTwo*nValueTwo; // 开方 dGrad=pow(dGrad,0.5); // 范数采用欧式距离 *(pdGrad+y*nWidth+x)=dGrad; } } /************************************************************************* * * \函数名称: * LaplacianOperator() * * \输入参数: * CDib * pDib - 指向CDib类的指针,含有原始图象信息 * double * pdGrad - 指向梯度数据的指针,含有图像的梯度信息 * * \返回值: * 无 * * \说明: * LaplacianOperator算子,是二阶算子,不想Roberts算子那样需要两个模板计算 * 梯度,LaplacianOperator算子只要一个算子就可以计算梯度。但是因为利用了 * 二阶信息,对噪声比较敏感 * ************************************************************************* */ void LaplacianOperator(CDib * pDib, double * pdGrad) { // 遍历图象的纵坐标 int y; // 遍历图象的横坐标 int x; // 图象的长宽大小 CSize sizeImage = pDib->GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; // 图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); // 图像在内存中每一行象素占用的实际空间 int nSaveWidth = sizeImageSave.cx; // 图像数据的指针 LPBYTE lpImage = pDib->m_lpImage; // 初始化 for(y=0; y<nHeight ; y++ ) for(x=0 ; x<nWidth ; x++ ) { *(pdGrad+y*nWidth+x)=0; } // 设置模板系数 static int nWeight[3][3] ; nWeight[0][0] = -1 ; nWeight[0][1] = -1 ; nWeight[0][2] = -1 ; nWeight[1][0] = -1 ; nWeight[1][1] = 8 ; nWeight[1][2] = -1 ; nWeight[2][0] = -1 ; nWeight[2][1] = -1 ; nWeight[2][2] = -1 ; //这个变量用来表示Laplacian算子象素值 int nTmp[3][3]; // 临时变量 double dGrad; // 模板循环控制变量 int yy ; int xx ; // 下面开始利用Laplacian算子进行计算,为了保证计算所需要的 // 的数据位于图像数据的内部,下面的两重循环的条件是 // y<nHeight-2 而不是y<nHeight,相应的x方向也是x<nWidth-2 // 而不是x<nWidth for(y=1; y<nHeight-2 ; y++ ) for(x=1 ; x<nWidth-2 ; x++ ) { dGrad = 0 ; // Laplacian算子需要的各点象素值 // 模板第一行 nTmp[0][0] = lpImage[(y-1)*nSaveWidth + x - 1 ] ; nTmp[0][1] = lpImage[(y-1)*nSaveWidth + x ] ; nTmp[0][2] = lpImage[(y-1)*nSaveWidth + x + 1 ] ; // 模板第二行 nTmp[1][0] = lpImage[y*nSaveWidth + x - 1 ] ; nTmp[1][1] = lpImage[y*nSaveWidth + x ] ; nTmp[1][2] = lpImage[y*nSaveWidth + x + 1 ] ; // 模板第三行 nTmp[2][0] = lpImage[(y+1)*nSaveWidth + x - 1 ] ; nTmp[2][1] = lpImage[(y+1)*nSaveWidth + x ] ; nTmp[2][2] = lpImage[(y+1)*nSaveWidth + x + 1 ] ; // 计算梯度 for(yy=0; yy<3; yy++) for(xx=0; xx<3; xx++) { dGrad += nTmp[yy][xx] * nWeight[yy][xx] ; } // 梯度值写入内存 *(pdGrad+y*nWidth+x)=dGrad; } } /************************************************************************* * * \函数名称: * RegionGrow() * * \输入参数: * CDib * pDib - 指向CDib类的指针,含有原始图象信息 * unsigned char * pUnRegion - 指向区域生长结果的指针 * * \返回值: * 无 * * \说明: * pUnRegion指针指向的数据区存储了区域生长的结果,其中1(逻辑)表示 * 对应象素为生长区域,0表示为非生长区域 * 区域生长一般包含三个比较重要的问题: * 1. 种子点的选取 * 2. 生长准则 * 3. 终止条件 * 可以认为,这三个问题需要具体分析,而且每个问题解决的好坏直接关系到 * 区域生长的结果。 * 本函数的种子点选取为图像的中心,生长准则是相邻象素的象素值小于 * nThreshold, 终止条件是一直进行到再没有满足生长准则需要的象素时为止 * ************************************************************************* */ void RegionGrow(CDib * pDib, unsigned char * pUnRegion, int nThreshold) { static int nDx[]={-1,0,1,0}; static int nDy[]={0,1,0,-1}; // 遍历图象的纵坐标 // int y; // 遍历图象的横坐标 // int x; // 图象的长宽大小 CSize sizeImage = pDib->GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; // 图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); // 图像在内存中每一行象素占用的实际空间 int nSaveWidth = sizeImageSave.cx; // 初始化 memset(pUnRegion,0,sizeof(unsigned char)*nWidth*nHeight); // 种子点 int nSeedX, nSeedY; // 设置种子点为图像的中心 nSeedX = nWidth /2 ; nSeedY = nHeight/2 ; // 定义堆栈,存储坐标 int * pnGrowQueX ; int * pnGrowQueY ; // 分配空间 pnGrowQueX = new int [nWidth*nHeight]; pnGrowQueY = new int [nWidth*nHeight]; // 图像数据的指针 unsigned char * pUnchInput =(unsigned char * )pDib->m_lpImage; // 定义堆栈的起点和终点 // 当nStart=nEnd, 表示堆栈中只有一个点 int nStart ; int nEnd ; //初始化 nStart = 0 ; nEnd = 0 ; // 把种子点的坐标压入栈 pnGrowQueX[nEnd] = nSeedX; pnGrowQueY[nEnd] = nSeedY; // 当前正在处理的象素 int nCurrX ; int nCurrY ; // 循环控制变量 int k ; // 图象的横纵坐标,用来对当前象素的4邻域进行遍历 int xx; int yy; while (nStart<=nEnd) { // 当前种子点的坐标 nCurrX = pnGrowQueX[nStart]; nCurrY = pnGrowQueY[nStart]; // 对当前点的4邻域进行遍历 for (k=0; k<4; k++) { // 4邻域象素的坐标 xx = nCurrX+nDx[k]; yy = nCurrY+nDy[k]; // 判断象素(xx,yy) 是否在图像内部 // 判断象素(xx,yy) 是否已经处理过 // pUnRegion[yy*nWidth+xx]==0 表示还没有处理 // 生长条件:判断象素(xx,yy)和当前象素(nCurrX,nCurrY) 象素值差的绝对值 if ( (xx < nWidth) && (xx>=0) && (yy<nHeight) && (yy>=0) && (pUnRegion[yy*nWidth+xx]==0) && abs(pUnchInput[yy*nSaveWidth+xx] - pUnchInput[nCurrY*nSaveWidth+nCurrX])<nThreshold ) { // 堆栈的尾部指针后移一位 nEnd++; // 象素(xx,yy) 压入栈 pnGrowQueX[nEnd] = xx; pnGrowQueY[nEnd] = yy; // 把象素(xx,yy)设置成逻辑1(255) // 同时也表明该象素处理过 pUnRegion[yy*nWidth+xx] = 255 ; } } nStart++; } // 释放内存 delete []pnGrowQueX; delete []pnGrowQueY; pnGrowQueX = NULL ; pnGrowQueY = NULL ; } void DFT_2D(CDib * pDib,double * pTrRstRpart, double * pTrRstIpart) { double PI = 3.14159; //遍历图象的纵坐标 int y; //遍历图象的横坐标 int x; //频域的横坐标 int m; //频域的纵坐标 int n; //图象的长宽大小 CSize sizeImage = pDib->GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; //图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); int nSaveWidth = sizeImageSave.cx; //图像数据的指针 LPBYTE pImageData = pDib->m_lpImage; //初始化结果数据 for(n=0; n<nHeight ; n++ ) for(m=0 ; m<nWidth ; m++ ) { *( pTrRstRpart + n*nWidth + m ) =0; *( pTrRstIpart + n*nWidth + m ) =0; } double fCosTable; double fSinTable; int nPxValue; fCosTable=0 ; nPxValue =0; double fTmpRstR; double fTmpRstI; for(n=0; n<nHeight ; n++ ) for(m=0 ; m<nWidth ; m++ ) { fTmpRstR=0; fTmpRstI=0; for(y=0; y<nHeight ; y++ ) for(x=0 ; x<nWidth ; x++ ) { fCosTable= cos( 2*PI*( ((double)m*x)/nWidth + ((double)n*y)/nHeight) ) ; fSinTable= sin( -2*PI*( ((double)m*x)/nWidth + ((double)n*y)/nHeight) ) ; nPxValue = *(pImageData+ y*nSaveWidth + x ) ; fTmpRstR+=fCosTable* nPxValue ; fTmpRstI+=fSinTable* nPxValue ; } *( pTrRstRpart + nWidth * n + m ) = fTmpRstR; *( pTrRstIpart + nWidth * n + m ) = fTmpRstI; } } void IDFT_2D(CDib * pDib,double * pTrRstRpart, double * pTrRstIpart) { double PI = 3.14159; //遍历图象的纵坐标 int y; //遍历图象的横坐标 int x; //频域的横坐标 int m; //频域的纵坐标 int n; //图象的长宽大小 CSize sizeImage = pDib->GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; //图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); int nSaveWidth = sizeImageSave.cx; //图像数据的指针 LPBYTE pImageData = pDib->m_lpImage; double fCosTable; double fSinTable; fCosTable=0 ; fSinTable=0 ; double fTmpPxValue; double fRpartValue; double fIpartValue; fTmpPxValue=0; fRpartValue=0; fIpartValue=0; for(y=0; y<nHeight ; y++ ) for(x=0 ; x<nWidth ; x++ ) { fTmpPxValue=0; for(n=0; n<nHeight ; n++ ) for(m=0 ; m<nWidth ; m++ ) { fCosTable= cos( 2*PI*( ((double)m*x)/nWidth + ((double)n*y)/nHeight) ) ; fSinTable= sin( 2*PI*( ((double)m*x)/nWidth + ((double)n*y)/nHeight) ) ; fRpartValue=*(pTrRstRpart+ n*nHeight + m ) ; fIpartValue=*(pTrRstIpart+ n*nHeight + m ) ; fTmpPxValue+=fCosTable* fRpartValue-fSinTable*fIpartValue; } fTmpPxValue=fTmpPxValue/(nHeight*nWidth); *( pImageData + nSaveWidth * y + x) = (unsigned char) fTmpPxValue ; } } /************************************************************************* * * \函数名称: * SobelOperator() * * \输入参数: * CDib * pDib - 指向CDib类的指针,含有原始图象信息 * double * pdGrad - 指向梯度数据的指针,含有图像的梯度信息 * * \返回值: * 无 * * \说明: * Sobe算子 * * 并行边界分割 * ************************************************************************* */ void SobelOperator(CDib * pDib, double * pdGrad) { // 遍历图象的纵坐标 int y; // 遍历图象的横坐标 int x; // 图象的长宽大小 CSize sizeImage = pDib->GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; // 图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); // 图像在内存中每一行象素占用的实际空间 int nSaveWidth = sizeImageSave.cx; // 图像数据的指针 LPBYTE lpImage = pDib->m_lpImage; // 初始化 for(y=0; y<nHeight ; y++ ) for(x=0 ; x<nWidth ; x++ ) { *(pdGrad+y*nWidth+x)=0; } // 设置模板系数 static int nWeight[2][3][3] ; nWeight[0][0][0] = -1 ; nWeight[0][0][1] = 0 ; nWeight[0][0][2] = 1 ; nWeight[0][1][0] = -2 ; nWeight[0][1][1] = 0 ; nWeight[0][1][2] = 2 ; nWeight[0][2][0] = -1 ; nWeight[0][2][1] = 0 ; nWeight[0][2][2] = 1 ; nWeight[1][0][0] = 1 ; nWeight[1][0][1] = 2 ; nWeight[1][0][2] = 1 ; nWeight[1][1][0] = 0 ; nWeight[1][1][1] = 0 ; nWeight[1][1][2] = 0 ; nWeight[1][2][0] = -1 ; nWeight[1][2][1] = -2 ; nWeight[1][2][2] = -1 ; //这个变量用来表示Laplacian算子象素值 int nTmp[3][3]; // 临时变量 double dGrad ; double dGradOne; double dGradTwo; // 模板循环控制变量 int yy ; int xx ; // 下面开始利用Prewitt算子进行计算,为了保证计算所需要的 // 的数据位于图像数据的内部,下面的两重循环的条件是 // y<nHeight-1 而不是y<nHeight,相应的x方向也是x<nWidth-1 // 而不是x<nWidth for(y=1; y<nHeight-1 ; y++ ) for(x=1 ; x<nWidth-1 ; x++ ) { dGrad = 0 ; dGradOne = 0 ; dGradTwo = 0 ; // Laplacian算子需要的各点象素值 // 模板第一行 nTmp[0][0] = lpImage[(y-1)*nSaveWidth + x - 1 ] ; nTmp[0][1] = lpImage[(y-1)*nSaveWidth + x ] ; nTmp[0][2] = lpImage[(y-1)*nSaveWidth + x + 1 ] ; // 模板第二行 nTmp[1][0] = lpImage[y*nSaveWidth + x - 1 ] ; nTmp[1][1] = lpImage[y*nSaveWidth + x ] ; nTmp[1][2] = lpImage[y*nSaveWidth + x + 1 ] ; // 模板第三行 nTmp[2][0] = lpImage[(y+1)*nSaveWidth + x - 1 ] ; nTmp[2][1] = lpImage[(y+1)*nSaveWidth + x ] ; nTmp[2][2] = lpImage[(y+1)*nSaveWidth + x + 1 ] ; // 计算梯度 for(yy=0; yy<3; yy++) for(xx=0; xx<3; xx++) { dGradOne += nTmp[yy][xx] * nWeight[0][yy][xx] ; dGradTwo += nTmp[yy][xx] * nWeight[1][yy][xx] ; } dGrad = dGradOne*dGradOne + dGradTwo*dGradTwo ; dGrad = sqrt(dGrad) ; // 梯度值写入内存 *(pdGrad+y*nWidth+x)=dGrad; } } /************************************************************************* * * \函数名称: * PrewittOperator() * * \输入参数: * CDib * pDib - 指向CDib类的指针,含有原始图象信息 * double * pdGrad - 指向梯度数据的指针,含有图像的梯度信息 * * \返回值: * 无 * * \说明: * Prewitt算子 * * 并行边界分割 * ************************************************************************* */ void PrewittOperator(CDib * pDib, double * pdGrad) { // 遍历图象的纵坐标 int y; // 遍历图象的横坐标 int x; // 图象的长宽大小 CSize sizeImage = pDib->GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; // 图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); // 图像在内存中每一行象素占用的实际空间 int nSaveWidth = sizeImageSave.cx; // 图像数据的指针 LPBYTE lpImage = pDib->m_lpImage; // 初始化 for(y=0; y<nHeight ; y++ ) for(x=0 ; x<nWidth ; x++ ) { *(pdGrad+y*nWidth+x)=0; } // 设置模板系数 static int nWeight[2][3][3] ; nWeight[0][0][0] = -1 ; nWeight[0][0][1] = 0 ; nWeight[0][0][2] = 1 ; nWeight[0][1][0] = -1 ; nWeight[0][1][1] = 0 ; nWeight[0][1][2] = 1 ; nWeight[0][2][0] = -1 ; nWeight[0][2][1] = 0 ; nWeight[0][2][2] = 1 ; nWeight[1][0][0] = 1 ; nWeight[1][0][1] = 1 ; nWeight[1][0][2] = 1 ; nWeight[1][1][0] = 0 ; nWeight[1][1][1] = 0 ; nWeight[1][1][2] = 0 ; nWeight[1][2][0] = -1 ; nWeight[1][2][1] = -1 ; nWeight[1][2][2] = -1 ; //这个变量用来表示Laplacian算子象素值 int nTmp[3][3]; // 临时变量 double dGrad ; double dGradOne; double dGradTwo; // 模板循环控制变量 int yy ; int xx ; // 下面开始利用Prewitt算子进行计算,为了保证计算所需要的 // 的数据位于图像数据的内部,下面的两重循环的条件是 // y<nHeight-1 而不是y<nHeight,相应的x方向也是x<nWidth-1 // 而不是x<nWidth for(y=1; y<nHeight-1 ; y++ ) for(x=1 ; x<nWidth-1 ; x++ ) { dGrad = 0 ; dGradOne = 0 ; dGradTwo = 0 ; // Laplacian算子需要的各点象素值 // 模板第一行 nTmp[0][0] = lpImage[(y-1)*nSaveWidth + x - 1 ] ; nTmp[0][1] = lpImage[(y-1)*nSaveWidth + x ] ; nTmp[0][2] = lpImage[(y-1)*nSaveWidth + x + 1 ] ; // 模板第二行 nTmp[1][0] = lpImage[y*nSaveWidth + x - 1 ] ; nTmp[1][1] = lpImage[y*nSaveWidth + x ] ; nTmp[1][2] = lpImage[y*nSaveWidth + x + 1 ] ; // 模板第三行 nTmp[2][0] = lpImage[(y+1)*nSaveWidth + x - 1 ] ; nTmp[2][1] = lpImage[(y+1)*nSaveWidth + x ] ; nTmp[2][2] = lpImage[(y+1)*nSaveWidth + x + 1 ] ; // 计算梯度 for(yy=0; yy<3; yy++) for(xx=0; xx<3; xx++) { dGradOne += nTmp[yy][xx] * nWeight[0][yy][xx] ; dGradTwo += nTmp[yy][xx] * nWeight[1][yy][xx] ; } dGrad = dGradOne*dGradOne + dGradTwo*dGradTwo ; dGrad = sqrt(dGrad) ; // 梯度值写入内存 *(pdGrad+y*nWidth+x)=dGrad; } } /************************************************************************* * * \函数名称: * EdgeTrack() * * \输入参数: * CDib * pDib - 指向CDib类的指针,含有原始图象信息 * unsigned char * pUnEdgeTrack - 指向边界跟踪结果的指针 * * \返回值: * 无 * * \说明: * pUnEdgeTrack指针指向的数据区存储了边界跟踪的结果,其中1(逻辑)表示 * 对应象素为边界点,0表示为非边界点 * * 串行边界分割 * ************************************************************************* */ void EdgeTrack(CDib * pDib, unsigned char * pUnEdgeTrack) { static int nDx[8]={-1,-1,-1, 0, 0, 1, 1, 1}; static int nDy[8]={-1, 0, 1,-1, 1,-1, 0, 1}; // 遍历图象的纵坐标 int y; // 遍历图象的横坐标 int x; // 图象的长宽大小 CSize sizeImage = pDib->GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; // 指向梯度数据的指针 double * pdGrad; // 按照图像的大小开辟内存空间,存储梯度计算的结果 pdGrad=new double[nHeight*nWidth]; // 调用Roberts算子求梯度 RobertsOperator(pDib, pdGrad); // 定义当前象素梯度值 double dCurrGrad = 0; // 定义最大梯度值 double dMaxGrad; // 设置初值 dMaxGrad = 0; // 最大梯度值对应的象素点坐标 int nPx; int nPy; nPx = 0; nPy = 0; // 求梯度最大值所在的象素点坐标 for(y=0; y<nHeight; y++) { for(x=0; x<nWidth; x++) { dCurrGrad = pdGrad[y*nWidth + x] ; if( dMaxGrad< dCurrGrad ) { dMaxGrad = dCurrGrad; nPx = x ; nPy = y ; } } } // 初始化 memset(pUnEdgeTrack,0,sizeof(unsigned char)*nWidth*nHeight); dCurrGrad = pdGrad[nPy*nWidth + nPx] ; // 从(nPx,nPy)点开始进行边界跟踪 pUnEdgeTrack[nPy*nWidth + nPx] = 255 ; // 循环变量,遍历当前象素的8邻域 int i ; int yy; int xx; int nDetX; int nDetY; while(dCurrGrad>10) { // 设置当前点为边界点 pUnEdgeTrack[nPy*nWidth + nPx] = 255 ; dMaxGrad = 0 ; for(i=0; i<8; i++) { nDetX=nDx[i]; nDetY=nDy[i]; y = nPy + nDetY; x = nPx + nDetX; // 判断是否在图像内部 if(x>=0 && x<nWidth && y>=0 && y<nHeight) { if( ( pdGrad[y*nWidth + x] > dMaxGrad) && ( pUnEdgeTrack[y*nWidth + x] == 0) ) { dMaxGrad = pdGrad[y*nWidth + x] ; yy = y; xx = x; } } } // 下一个边界点的梯度,横纵坐标 dCurrGrad = dMaxGrad ; nPy = yy; nPx = xx; } //释放内存 delete pdGrad; pdGrad = NULL; } /************************************************************************* * * \函数名称: * MakeGauss() * * \输入参数: * double sigma - 高斯函数的标准差 * double **pdKernel - 指向高斯数据数组的指针 * int *pnWindowSize - 数据的长度 * * \返回值: * 无 * * \说明: * 这个函数可以生成一个一维的高斯函数的数字数据,理论上高斯数据的长度应 * 该是无限长的,但是为了计算的简单和速度,实际的高斯数据只能是有限长的 * pnWindowSize就是数据长度 * ************************************************************************* */ void MakeGauss(double sigma, double **pdKernel, int *pnWindowSize) { // 循环控制变量 int i ; // 数组的中心点 int nCenter; // 数组的某一点到中心点的距离 double dDis ; double PI = 3.14159; // 中间变量 double dValue; double dSum ; dSum = 0 ; // 数组长度,根据概率论的知识,选取[-3*sigma, 3*sigma]以内的数据。 // 这些数据会覆盖绝大部分的滤波系数 *pnWindowSize = 1 + 2 * ceil(3 * sigma); // 中心 nCenter = (*pnWindowSize) / 2; // 分配内存 *pdKernel = new double[*pnWindowSize] ; for(i=0; i< (*pnWindowSize); i++) { dDis = (double)(i - nCenter); dValue = exp(-(1/2)*dDis*dDis/(sigma*sigma)) / (sqrt(2 * PI) * sigma ); (*pdKernel)[i] = dValue ; dSum += dValue; } // 归一化 for(i=0; i<(*pnWindowSize) ; i++) { (*pdKernel)[i] /= dSum; } } /************************************************************************* * * \函数名称: * GaussianSmooth() * * \输入参数: * unsigned char * pUnchImg - 指向图象数据的指针 * int nWidth - 图象数据宽度 * int nHeight - 图象数据高度 * double dSigma - 高斯函数的标准差 * unsigned char * pUnchSmthdImg - 指向经过平滑之后的图象数据 * * \返回值: * 无 * * \说明: * 为了抑止噪声,采用高斯滤波对图象进行滤波,滤波先对x方向进行,然后对 * y方向进行。 * ************************************************************************* */ void GaussianSmooth(unsigned char *pUnchImg, int nWidth, int nHeight, double sigma, unsigned char * pUnchSmthdImg) { // 循环控制变量 int y; int x; int i; // 高斯滤波器的数组长度 int nWindowSize; // 窗口长度的1/2 int nHalfLen; // 一维高斯数据滤波器 double *pdKernel ; // 高斯系数与图象数据的点乘 double dDotMul ; // 高斯滤波系数的总和 double dWeightSum ; // 中间变量 double * pdTmp ; // 分配内存 pdTmp = new double[nWidth*nHeight]; // 产生一维高斯数据滤波器 // MakeGauss(sigma, &dKernel, &nWindowSize); MakeGauss(sigma, &pdKernel, &nWindowSize) ; // MakeGauss返回窗口的长度,利用此变量计算窗口的半长 nHalfLen = nWindowSize / 2; // x方向进行滤波 for(y=0; y<nHeight; y++) { for(x=0; x<nWidth; x++) { dDotMul = 0; dWeightSum = 0; for(i=(-nHalfLen); i<=nHalfLen; i++) { // 判断是否在图象内部 if( (i+x) >= 0 && (i+x) < nWidth ) { dDotMul += (double)pUnchImg[y*nWidth + (i+x)] * pdKernel[nHalfLen+i]; dWeightSum += pdKernel[nHalfLen+i]; } } pdTmp[y*nWidth + x] = dDotMul/dWeightSum ; } } // y方向进行滤波 for(x=0; x<nWidth; x++) { for(y=0; y<nHeight; y++) { dDotMul = 0; dWeightSum = 0; for(i=(-nHalfLen); i<=nHalfLen; i++) { // 判断是否在图象内部 if( (i+y) >= 0 && (i+y) < nHeight ) { dDotMul += (double)pdTmp[(y+i)*nWidth + x] * pdKernel[nHalfLen+i]; dWeightSum += pdKernel[nHalfLen+i]; } } pUnchSmthdImg[y*nWidth + x] = (unsigned char)(int)dDotMul/dWeightSum ; } } // 释放内存 delete []pdKernel; pdKernel = NULL ; delete []pdTmp; pdTmp = NULL; } /************************************************************************* * * \函数名称: * DirGrad() * * \输入参数: * unsigned char *pUnchSmthdImg - 经过高斯滤波后的图象 * int nWidht - 图象宽度 * int nHeight - 图象高度 * int *pnGradX - x方向的方向导数 * int *pnGradY - y方向的方向导数 * \返回值: * 无 * * \说明: * 这个函数计算方向倒数,采用的微分算子是(-1 0 1) 和 (-1 0 1)'(转置) * 计算的时候对边界象素采用了特殊处理 * * ************************************************************************* */ void DirGrad(unsigned char *pUnchSmthdImg, int nWidth, int nHeight, int *pnGradX , int *pnGradY) { // 循环控制变量 int y ; int x ; // 计算x方向的方向导数,在边界出进行了处理,防止要访问的象素出界 for(y=0; y<nHeight; y++) { for(x=0; x<nWidth; x++) { pnGradX[y*nWidth+x] = (int) ( pUnchSmthdImg[y*nWidth+min(nWidth-1,x+1)] - pUnchSmthdImg[y*nWidth+max(0,x-1)] ); } } // 计算y方向的方向导数,在边界出进行了处理,防止要访问的象素出界 for(x=0; x<nWidth; x++) { for(y=0; y<nHeight; y++) { pnGradY[y*nWidth+x] = (int) ( pUnchSmthdImg[min(nHeight-1,y+1)*nWidth + x] - pUnchSmthdImg[max(0,y-1)*nWidth+ x ] ); } } } /************************************************************************* * * \函数名称: * GradMagnitude() * * \输入参数: * int *pnGradX - x方向的方向导数 * int *pnGradY - y方向的方向导数 * int nWidht - 图象宽度 * int nHeight - 图象高度 * int *pnMag - 梯度幅度 * * \返回值: * 无 * * \说明: * 这个函数利用方向倒数计算梯度幅度,方向倒数是DirGrad函数计算的结果 * ************************************************************************* */ void GradMagnitude(int *pnGradX, int *pnGradY, int nWidth, int nHeight, int *pnMag) { // 循环控制变量 int y ; int x ; // 中间变量 double dSqtOne; double dSqtTwo; for(y=0; y<nHeight; y++) { for(x=0; x<nWidth; x++) { dSqtOne = pnGradX[y*nWidth + x] * pnGradX[y*nWidth + x]; dSqtTwo = pnGradY[y*nWidth + x] * pnGradY[y*nWidth + x]; pnMag[y*nWidth + x] = (int)(sqrt(dSqtOne + dSqtTwo) + 0.5); } } } /************************************************************************* * * \函数名称: * NonmaxSuppress() * * \输入参数: * int *pnMag - 梯度图 * int *pnGradX - x方向的方向导数 * int *pnGradY - y方向的方向导数 * int nWidth - 图象数据宽度 * int nHeight - 图象数据高度 * unsigned char *pUnchRst - 经过NonmaxSuppress处理后的结果 * * \返回值: * 无 * * \说明: * 抑止梯度图中非局部极值点的象素。 * ************************************************************************* */ void NonmaxSuppress(int *pnMag, int *pnGradX, int *pnGradY, int nWidth, int nHeight, unsigned char *pUnchRst) { // 循环控制变量 int y ; int x ; int nPos; // x方向梯度分量 int gx ; int gy ; // 临时变量 int g1, g2, g3, g4 ; double weight ; double dTmp1 ; double dTmp2 ; double dTmp ; // 设置图象边缘部分为不可能的边界点 for(x=0; x<nWidth; x++) { pUnchRst[x] = 0 ; pUnchRst[nHeight-1+x] = 0; } for(y=0; y<nHeight; y++) { pUnchRst[y*nWidth] = 0 ; pUnchRst[y*nWidth + nWidth-1] = 0; } for(y=1; y<nHeight-1; y++) { for(x=1; x<nWidth-1; x++) { nPos = y*nWidth + x ; // 如果当前象素的梯度幅度为0,则不是边界点 if(pnMag[nPos] == 0 ) { pUnchRst[nPos] = 0 ; } else { // 当前象素的梯度幅度 dTmp = pnMag[nPos] ; // x,y方向导数 gx = pnGradX[nPos] ; gy = pnGradY[nPos] ; // 如果方向导数y分量比x分量大,说明导数的方向更加“趋向”于y分量。 if (abs(gy) > abs(gx)) { // 计算插值的比例 weight = fabs(gx)/fabs(gy); g2 = pnMag[nPos-nWidth] ; g4 = pnMag[nPos+nWidth] ; // 如果x,y两个方向的方向导数的符号相同 // C是当前象素,与g1-g4的位置关系为: // g1 g2 // C // g4 g3 if (gx*gy > 0) { g1 = pnMag[nPos-nWidth-1] ; g3 = pnMag[nPos+nWidth+1] ; } // 如果x,y两个方向的方向导数的符号相反 // C是当前象素,与g1-g4的位置关系为: // g2 g1 // C // g3 g4 else { g1 = pnMag[nPos-nWidth+1] ; g3 = pnMag[nPos+nWidth-1] ; } } // 如果方向导数x分量比y分量大,说明导数的方向更加“趋向”于x分量 // 这个判断语句包含了x分量和y分量相等的情况 else { // 计算插值的比例 weight = fabs(gy)/fabs(gx); g2 = pnMag[nPos+1] ; g4 = pnMag[nPos-1] ; // 如果x,y两个方向的方向导数的符号相同 // C是当前象素,与g1-g4的位置关系为: // g3 // g4 C g2 // g1 if (gx*gy > 0) { g1 = pnMag[nPos+nWidth+1] ; g3 = pnMag[nPos-nWidth-1] ; } // 如果x,y两个方向的方向导数的符号相反 // C是当前象素,与g1-g4的位置关系为: // g1 // g4 C g2 // g3 else { g1 = pnMag[nPos-nWidth+1] ; g3 = pnMag[nPos+nWidth-1] ; } } // 下面利用g1-g4对梯度进行插值 { dTmp1 = weight*g1 + (1-weight)*g2 ; dTmp2 = weight*g3 + (1-weight)*g4 ; // 当前象素的梯度是局部的最大值 // 该点可能是个边界点 if(dTmp>=dTmp1 && dTmp>=dTmp2) { pUnchRst[nPos] = 128 ; } else { // 不可能是边界点 pUnchRst[nPos] = 0 ; } } } //else } // for } } /************************************************************************* * * \函数名称: * TraceEdge() * * \输入参数: * int x - 跟踪起点的x坐标 * int y - 跟踪起点的y坐标 * int nLowThd - 判断一个点是否为边界点的低阈值 * unsigned char *pUnchEdge - 记录边界点的缓冲区 * int *pnMag - 梯度幅度图 * int nWidth - 图象数据宽度 * * \返回值: * 无 * * \说明: * 递归调用 * 从(x,y)坐标出发,进行边界点的跟踪,跟踪只考虑pUnchEdge中没有处理并且 * 可能是边界点的那些象素(=128),象素值为0表明该点不可能是边界点,象素值 * 为255表明该点已经被设置为边界点,不必再考虑 * * ************************************************************************* */ void TraceEdge (int y, int x, int nLowThd, unsigned char *pUnchEdge, int *pnMag, int nWidth) { // 对8邻域象素进行查询 int xNb[8] = {1, 1, 0,-1,-1,-1, 0, 1} ; int yNb[8] = {0, 1, 1, 1,0 ,-1,-1,-1} ; int yy ; int xx ; int k ; for(k=0; k<8; k++) { yy = y + yNb[k] ; xx = x + xNb[k] ; // 如果该象素为可能的边界点,又没有处理过 // 并且梯度大于阈值 if(pUnchEdge[yy*nWidth+xx] == 128 && pnMag[yy*nWidth+xx]>=nLowThd) { // 把该点设置成为边界点 pUnchEdge[yy*nWidth+xx] = 255 ; // 以该点为中心进行跟踪 TraceEdge(yy, xx, nLowThd, pUnchEdge, pnMag, nWidth); } } } /************************************************************************* * * \函数名称: * EstimateThreshold() * * \输入参数: * int *pnMag - 梯度幅度图 * int nWidth - 图象数据宽度 * int nHeight - 图象数据高度 * int *pnThdHigh - 高阈值 * int *pnThdLow - 低阈值 * double dRatioLow - 低阈值和高阈值之间的比例 * double dRatioHigh - 高阈值占图象象素总数的比例 * unsigned char *pUnchEdge - 经过non-maximum处理后的数据 * * \返回值: * 无 * * \说明: * 经过non-maximum处理后的数据pUnchEdge,统计pnMag的直方图,确定阈值。 * 本函数中只是统计pUnchEdge中可能为边界点的那些象素。然后利用直方图, * 根据dRatioHigh设置高阈值,存储到pnThdHigh。利用dRationLow和高阈值, * 设置低阈值,存储到*pnThdLow。dRatioHigh是一种比例:表明梯度小于 * *pnThdHigh的象素数目占象素总数目的比例。dRationLow表明*pnThdHigh * 和*pnThdLow的比例,这个比例在canny算法的原文里,作者给出了一个区间。 * ************************************************************************* */ void EstimateThreshold(int *pnMag, int nWidth, int nHeight, int *pnThdHigh,int *pnThdLow, unsigned char * pUnchEdge, double dRatioHigh, double dRationLow) { // 循环控制变量 int y; int x; int k; // 该数组的大小和梯度值的范围有关,如果采用本程序的算法,那么梯度的范围不会超过pow(2,10) int nHist[1024] ; // 可能的边界数目 int nEdgeNb ; // 最大梯度值 int nMaxMag ; int nHighCount ; nMaxMag = 0 ; // 初始化 for(k=0; k<1024; k++) { nHist[k] = 0; } // 统计直方图,然后利用直方图计算阈值 for(y=0; y<nHeight; y++) { for(x=0; x<nWidth; x++) { // 只是统计那些可能是边界点,并且还没有处理过的象素 if(pUnchEdge[y*nWidth+x]==128) { nHist[ pnMag[y*nWidth+x] ]++; } } } nEdgeNb = nHist[0] ; nMaxMag = 0 ; // 统计经过“非最大值抑止(non-maximum suppression)”后有多少象素 for(k=1; k<1024; k++) { if(nHist[k] != 0) { // 最大梯度值 nMaxMag = k; } // 梯度为0的点是不可能为边界点的 // 经过non-maximum suppression后有多少象素 nEdgeNb += nHist[k]; } // 梯度比高阈值*pnThdHigh小的象素点总数目 nHighCount = (int)(dRatioHigh * nEdgeNb +0.5); k = 1; nEdgeNb = nHist[1]; // 计算高阈值 while( (k<(nMaxMag-1)) && (nEdgeNb < nHighCount) ) { k++; nEdgeNb += nHist[k]; } // 设置高阈值 *pnThdHigh = k ; // 设置低阈值 *pnThdLow = (int)((*pnThdHigh) * dRationLow+ 0.5); } /************************************************************************* * * \函数名称: * Hysteresis() * * \输入参数: * int *pnMag - 梯度幅度图 * int nWidth - 图象数据宽度 * int nHeight - 图象数据高度 * double dRatioLow - 低阈值和高阈值之间的比例 * double dRatioHigh - 高阈值占图象象素总数的比例 * unsigned char *pUnchEdge - 记录边界点的缓冲区 * * \返回值: * 无 * * \说明: * 本函数实现类似“磁滞现象”的一个功能,也就是,先调用EstimateThreshold * 函数对经过non-maximum处理后的数据pUnchSpr估计一个高阈值,然后判断 * pUnchSpr中可能的边界象素(=128)的梯度是不是大于高阈值nThdHigh,如果比 * 该阈值大,该点将作为一个边界的起点,调用TraceEdge函数,把对应该边界 * 的所有象素找出来。最后,当整个搜索完毕时,如果还有象素没有被标志成 * 边界点,那么就一定不是边界点。 * ************************************************************************* */ void Hysteresis(int *pnMag, int nWidth, int nHeight, double dRatioLow, double dRatioHigh, unsigned char *pUnchEdge) { // 循环控制变量 int y; int x; int nThdHigh ; int nThdLow ; int nPos; // 估计TraceEdge需要的低阈值,以及Hysteresis函数使用的高阈值 EstimateThreshold(pnMag, nWidth, nHeight, &nThdHigh, &nThdLow, pUnchEdge,dRatioHigh, dRatioLow); // 这个循环用来寻找大于nThdHigh的点,这些点被用来当作边界点,然后用 // TraceEdge函数来跟踪该点对应的边界 for(y=0; y<nHeight; y++) { for(x=0; x<nWidth; x++) { nPos = y*nWidth + x ; // 如果该象素是可能的边界点,并且梯度大于高阈值,该象素作为 // 一个边界的起点 if((pUnchEdge[nPos] == 128) && (pnMag[nPos] >= nThdHigh)) { // 设置该点为边界点 pUnchEdge[nPos] = 255; TraceEdge(y, x, nThdLow, pUnchEdge, pnMag, nWidth); } } } // 那些还没有被设置为边界点的象素已经不可能成为边界点 for(y=0; y<nHeight; y++) { for(x=0; x<nWidth; x++) { nPos = y*nWidth + x ; if(pUnchEdge[nPos] != 255) { // 设置为非边界点 pUnchEdge[nPos] = 0 ; } } } } /************************************************************************* * * \函数名称: * Canny() * * \输入参数: * unsigned char *pUnchImage- 图象数据 * int nWidth - 图象数据宽度 * int nHeight - 图象数据高度 * double sigma - 高斯滤波的标准方差 * double dRatioLow - 低阈值和高阈值之间的比例 * double dRatioHigh - 高阈值占图象象素总数的比例 * unsigned char *pUnchEdge - canny算子计算后的分割图 * * \返回值: * 无 * * \说明: * canny分割算子,计算的结果保存在pUnchEdge中,逻辑1(255)表示该点为 * 边界点,逻辑0(0)表示该点为非边界点。该函数的参数sigma,dRatioLow * dRatioHigh,是需要指定的。这些参数会影响分割后边界点数目的多少 ************************************************************************* */ void Canny(unsigned char *pUnchImage, int nWidth, int nHeight, double sigma, double dRatioLow, double dRatioHigh, unsigned char *pUnchEdge) { // 经过高斯滤波后的图象数据 unsigned char * pUnchSmooth ; // 指向x方向导数的指针 int * pnGradX ; // 指向y方向导数的指针 int * pnGradY ; // 梯度的幅度 int * pnGradMag ; pUnchSmooth = new unsigned char[nWidth*nHeight] ; pnGradX = new int [nWidth*nHeight] ; pnGradY = new int [nWidth*nHeight] ; pnGradMag = new int [nWidth*nHeight] ; // 对原图象进行滤波 GaussianSmooth(pUnchImage, nWidth, nHeight, sigma, pUnchSmooth) ; // 计算方向导数 DirGrad(pUnchSmooth, nWidth, nHeight, pnGradX, pnGradY) ; // 计算梯度的幅度 GradMagnitude(pnGradX, pnGradY, nWidth, nHeight, pnGradMag) ; // 应用non-maximum 抑制 NonmaxSuppress(pnGradMag, pnGradX, pnGradY, nWidth, nHeight, pUnchEdge) ; // 应用Hysteresis,找到所有的边界 Hysteresis(pnGradMag, nWidth, nHeight, dRatioLow, dRatioHigh, pUnchEdge); // 释放内存 delete pnGradX ; pnGradX = NULL ; delete pnGradY ; pnGradY = NULL ; delete pnGradMag ; pnGradMag = NULL ; delete pUnchSmooth ; pUnchSmooth = NULL ; }